package indi.zhifa.recipe.bailan.framework.enums.service.impl;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.extra.spring.SpringUtil;
import indi.zhifa.recipe.bailan.framework.enums.entity.annotations.EnumDesc;
import indi.zhifa.recipe.bailan.framework.enums.entity.vo.EnumVo;
import indi.zhifa.recipe.bailan.framework.enums.entity.vo.EnumItemVo;
import indi.zhifa.recipe.bailan.framework.enums.entity.vo.EnumNodeVo;
import indi.zhifa.recipe.bailan.framework.enums.entity.vo.IEnumVo;
import indi.zhifa.recipe.bailan.framework.enums.entity.vo.SysTypeVo;
import indi.zhifa.recipe.bailan.framework.enums.service.IEnumMemoService;
import indi.zhifa.recipe.bailan.framework.common.entity.exception.ServiceException;
import indi.zhifa.recipe.bailan.framework.enums.service.ISysTypeMemo;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

@Service
public class EnumMemoServiceImpl implements IEnumMemoService {

    public Map<String, EnumVo> enumsByName;
    public Map<Class, EnumNodeVo> enumMemoByClas;
    private final ISysTypeMemo mSysTypeMemo;

    public EnumMemoServiceImpl(){
        enumsByName = new HashMap<String, EnumVo>();
        enumMemoByClas = new HashMap<Class, EnumNodeVo>();
        mSysTypeMemo = SpringUtil.getBean(SysTypeMemoImpl.class);
    }

    @Override
    public void initEnum(String[] pEnumBasePackages) throws InvocationTargetException, IllegalAccessException {
        List<Class<?>> classes = new ArrayList<>();
        for(String path : pEnumBasePackages){
            Set<Class<?>> cls = ClassUtil.scanPackage(path);
            classes.addAll(cls);
        }
        for(Class cls : classes){
            if(!ClassUtil.isEnum(cls)){
                continue;
            }
            EnumDesc enumDesc = AnnotationUtils.findAnnotation(cls,EnumDesc.class);
            if(null == enumDesc){
                continue;
            }
            EnumVo enumDto = new EnumVo();
            String enumCode = cls.getSimpleName();
            enumDto.setCode(enumCode);
            enumDto.setName(enumDesc.name());
            enumDto.setDescription(enumDesc.desc());
            enumDto.setDefaultIdx(enumDesc.defaultIdx());
            enumDto.setDefaultItem(enumDesc.defaultItem());
            Object[] constants = cls.getEnumConstants();
            Method codeMethod = ClassUtil.getDeclaredMethod(cls, "getCode");
            Method nameMethod = ClassUtil.getDeclaredMethod(cls, "getName");
            Method descMethod = ClassUtil.getDeclaredMethod(cls, "getDescription");
            List<EnumItemVo> enumItemDtoList = new ArrayList<>();
            for(Object obj : constants){
                String enumName = obj.toString();
                if(enumName.equals("DEFAULT")){
                    continue;
                }
                EnumItemVo enumItemDto = new EnumItemVo();
                enumItemDto.setName(enumName);
                int code = (int)codeMethod.invoke(obj);
                String name = (String)nameMethod.invoke(obj);
                String desc = (String)descMethod.invoke(obj);
                enumItemDto.setCode(code);
                enumItemDto.setCname(name);
                enumItemDto.setDescription(desc);
                enumItemDtoList.add(enumItemDto);
            }
            enumDto.setItems(enumItemDtoList.toArray(new EnumItemVo[0]));
            enumsByName.put(enumDto.getCode(),enumDto);
            EnumNodeVo enumNodeDto = new EnumNodeVo(enumDto);
            enumMemoByClas.put(cls,enumNodeDto);

        }
    }

    @Override
    public IEnumVo findByName(String pName) throws ServiceException {
        EnumVo enumDto = enumsByName.get(pName);
        if(null == enumDto){
            SysTypeVo sysTypeVo = mSysTypeMemo.infoFromMemo(pName);
            if(null == sysTypeVo){
                throw new ServiceException("没有找到名为"+pName+"的枚举或类型");
            }
            return sysTypeVo;
        }
        return enumDto;
    }

    @Override
    public EnumNodeVo findByClass(Class pCls) {
        EnumNodeVo enumNodeDto = enumMemoByClas.get(pCls);
        return enumNodeDto;
    }

    @Override
    public EnumVo[] list() {
        EnumVo[] enumDtoArray = enumsByName.values().toArray(new EnumVo[0]);
        return enumDtoArray;
    }

    @Override
    public void addEnumDto(EnumVo pEnumDto) {
        if(!enumsByName.containsKey(pEnumDto.getCode())){
            enumsByName.put(pEnumDto.getCode(),pEnumDto);
        }
    }
}
